test: harden clear() namespace isolation, tag invalidation and deleteItem() return value - #124
Conversation
Add an optional createCachePoolWithOtherNamespace() extension point and a test that saves the same key in two pools sharing the same storage under different namespaces. The test is skipped when the implementation returns null.
The test used overlapping tags, so an implementation that only invalidates the first tag of the list still passed. Use disjoint tags plus a control item tagged with none of the invalidated tags.
The return value was ignored, so an implementation returning false while the deletion actually happened still passed.
9f64b72 to
e61cecb
Compare
|
Nice find on all three. Few things before I merge.
Small one: On |
The final $other->clear() only ran when every assertion above it passed, so a failure left the second namespace populated for the next test.
key carries tag1 and tag2 again, which is the only place in the suite where one invalidateTags() call matches an item through two of the passed tags. key2 carries tag2 alone and key3 carries tag1 alone, so each invalidated tag is still required on its own. key4 carries none of them and checks that the invalidation does not reach too far.
TaggableCachePoolTest gains the same optional createCachePoolWithOtherNamespace() as CachePoolTest, returning null by default so the test is skipped on implementations that cannot provide a second pool. The TaggableCachePool fixture now takes an optional shared storage and a namespace, and wraps the storage in a ProxyAdapter. Both pools then sit on one key space where the namespace is a real key prefix, so the test can detect a tag invalidation that reaches across namespaces.
|
Thank you for this review, it is genuinely helpful. You read the actual mechanics of each adapter rather than just the diff, and the Points 2 and 3 are fixed.
On On the namespace test, I would rather not call it a smoke test, because passing is the expected outcome for a correct implementation. You are right that the suite here did not exercise a shared substrate, so nothing proved the test would catch a prefix collision. I addressed that on the taggable side, which is where I think it matters most: If you would also like the PSR-6 |
|
The The shared-storage fixture doesn't do what the docblock says, though. Probe against the fixture exactly as the test builds it: Two pools, two separate stores. And dropping the prefix doesn't test the prefix: with Which means the new test can't catch a cross-namespace leak. I made The fix is to wrap the storage in the return new TaggableCachePool(new Fixtures\ValidatingCachePool($this->sharedStorage()), 'first');
return new TaggableCachePool(new Fixtures\ValidatingCachePool($this->sharedStorage()), 'second');
|
Three small additions to the contract, found while implementing a PSR-6 adapter for MongoDB (symfony/symfony#65468). In each case the current tests pass on an implementation that is actually wrong.
clear() must not touch another namespace on the same storage
When several pools share a single storage, a table or a collection for instance, clearing one pool must only remove its own keys. Nothing checks that today, because the tests only ever use one pool.
CachePoolTestgains an optionalcreateCachePoolWithOtherNamespace(), which returns a second pool on the same storage under another namespace. It returns null by default, and the newtestClearDoesNotAffectOtherNamespacesis then skipped, so existing implementations are unaffected. When a pool is given, the test writes the same key in both, clears the first one, and checks that the second still holds its own value.testInvalidateTags now uses disjoint tags
keywas tagged with tag1 and tag2 whilekey2was tagged with tag1, so invalidating tag1 alone was enough to pass. Each item now carries a single distinct tag, and a third item, tagged with none of the invalidated tags, checks that the invalidation does not reach too far.deleteItem() return value on a tagged item
testRemoveTagWhenItemIsRemovedignored whatdeleteItem()returned. It is now asserted to be true.